package pl.edu.szlif.mosm.j2melib;

import J2MEGenericHttpConnectionImpl;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

import pl.edu.szlif.mosm.commonlib.provider.OSMTilesProvider;
import pl.edu.szlif.mosm.commonlib.provider.TilesProvider.TilesProviderListener;

/**
 * OSM widget implementation
 * 
 * @author Adam Morawski
 */
public class OSMWidgetImpl extends Canvas implements OSMWidget, TilesProviderListener {
	private OSMTilesProvider otp;
	private TileCache cache;
	private double xCoor; // TODO: center of screen, not top-left corner
	private double yCoor;
	private int zoom;
	
	public OSMWidgetImpl(int cacheSize) {
		otp = new OSMTilesProvider(new J2MEGenericHttpConnectionImpl());
		cache = new BasicTileCache(cacheSize);
	}
	
	private void refresh() {
		int tw = otp.getTileWidth();
		int th = otp.getTileHeight();
		int offx = (int) ((((double)((int)xCoor)) - xCoor) * tw);
		int offy = (int) ((((double)((int)yCoor)) - yCoor) * th);
		int pi=0; 
		for(int i=0; i<this.getWidth()-offx; i+=tw) {
			int pj=0;
			for(int j=0; j<this.getHeight()-offy; j+=th) {
				int tx = (int) xCoor + pi;
				int ty = (int) yCoor + pj;
				if(!(cache.contains(tx, ty, zoom)))
					otp.requestTile(tx, ty, zoom, this);
				pj++;
			}
			pi++;
		}
		this.repaint();
	}
	
	public void setZoom(int z) {
		if((z<=maxZoom()) && (z>=1)) {
			double lon = this.getLon();
			double lat = this.getLat();
			this.zoom = z;
			//this.geoScrollTo(lon, lat); // TODO - jak sie bedzie dobrze wyliczac
		}
	}

	public boolean canScrollBy(double x, double y) {
		return canScrollTo(x+xCoor, y+yCoor);
	}

	public void geoScrollTo(double lon, double lat) {
		scrollTo(otp.calculateTileX(lon, lat, zoom), otp.calculateTileY(lon, lat, zoom));
	}
	
	public double getLat() {
		return otp.calculateLat(xCoor, yCoor, zoom);
	}

	public double getLon() {
		return otp.calculateLon(xCoor, yCoor, zoom);
	}

	public double getX() {
		return xCoor;
	}

	public double getY() {
		return yCoor;
	}

	public int getZoom() {
		return zoom;
	}

	public double maxX() {
		return otp.calculateTileX(180.0, 180.0, zoom);
	}

	public double maxY() {
		return otp.calculateTileY(180.0, 180.0, zoom);
	}

	public int maxZoom() {
		return otp.getMaxZoom();
	}

	public void scrollBy(double x, double y) {
		if(canScrollBy(x, y))
			scrollTo(xCoor+x, yCoor+y);
	}

	private boolean canScrollTo(double x, double y) {
		return (x>=0) && (y>=0) && (x<=maxX()) && (y<=maxY());
	}

	public void scrollTo(double x, double y) {
		xCoor = x;
		yCoor = y;
		System.out.print(x);
		System.out.print(" ");
		System.out.println(y);
		this.refresh();
	}

	public void tileNotAvailable(int tx, int ty, int zoom) {
		return;
	}

	public void tileReady(int tx, int ty, int zoom, Image tile) {
		cache.put(tx, ty, zoom, tile);
		this.repaint();
	}

	protected void paint(Graphics g) {
		int tw = otp.getTileWidth();
		int th = otp.getTileHeight();
		int offx = (int) ((((double)((int)xCoor)) - xCoor) * tw);
		int offy = (int) ((((double)((int)yCoor)) - yCoor) * th);
		int pi=0; 
		for(int i=g.getClipX(); i<g.getClipWidth()-offx; i+=tw) {
			int pj = 0;
			for(int j=g.getClipY(); j<g.getClipHeight()-offy; j+=th) {
				int tx = (int) xCoor + pi;
				int ty = (int) yCoor + pj;
				int rx = pi*tw+offx;
				int ry = pj*th+offy;
				if(cache.contains(tx, ty, zoom))
					g.drawImage((Image) cache.get(tx, ty, zoom), rx, ry, 0);
				else {
					g.setColor(0xCCCCCC);
					g.fillRect(rx, ry, tw, th);
				}
				pj++;
			}
			pi++;
		}
	}
	
    protected void keyPressed(int keyCode){
    	super.keyPressed(keyCode);
    	System.out.print(".");
		switch(keyCode) {
	    	case LEFT:
	    		this.scrollBy(-0.5, 0.0);
	    	case RIGHT:
	    		this.scrollBy(0.5, 0.0);
	    	case DOWN:
	    		this.scrollBy(0.0, 0.5);
	    	case UP:
	    		this.scrollBy(0.0, -0.5);
		}
    }

}
